use serde::de::DeserializeOwned;
use tauri::{
    plugin::{PluginApi, PluginHandle},
    AppHandle, Runtime,
};

use crate::{models::*, Result};

#[cfg(target_os = "ios")]
tauri::ios_plugin_binding!(init_plugin_libsql);

// initializes the Kotlin or Swift plugin classes
pub fn init<R: Runtime, C: DeserializeOwned>(
    _app: &AppHandle<R>,
    api: PluginApi<R, C>,
) -> crate::Result<Libsql<R>> {
    #[cfg(target_os = "android")]
    let handle = api.register_android_plugin("app.tauri", "LibsqlPlugin")?;
    #[cfg(target_os = "ios")]
    let handle = api.register_ios_plugin(init_plugin_libsql)?;
    Ok(Libsql(handle))
}

/// Access to the libsql APIs.
pub struct Libsql<R: Runtime>(PluginHandle<R>);

impl<R: Runtime> Libsql<R> {
    pub async fn connect(&self, options: ConnectOptions) -> Result<String> {
        self.0
            .run_mobile_plugin("connect", options)
            .map_err(Into::into)
    }

    pub async fn execute(&self, options: ExecuteOptions) -> Result<u64> {
        self.0
            .run_mobile_plugin("execute", options)
            .map_err(Into::into)
    }

    pub async fn query(&self, options: QueryOptions) -> Result<QueryResult> {
        self.0
            .run_mobile_plugin("query", options)
            .map_err(Into::into)
    }

    pub async fn sync(&self, options: SyncOptions) -> Result<()> {
        self.0
            .run_mobile_plugin("sync", options)
            .map_err(Into::into)
    }

    pub async fn close(&self, options: CloseOptions) -> Result<()> {
        self.0
            .run_mobile_plugin("close", options)
            .map_err(Into::into)
    }

    // Vector operations for mobile platforms

    /// Execute a vector top-k nearest neighbor search
    pub async fn vector_top_k(&self, options: VectorTopKOptions) -> Result<QueryResult> {
        self.0
            .run_mobile_plugin("vector_top_k", options)
            .map_err(Into::into)
    }

    /// Calculate cosine distance between two vectors
    pub async fn vector_distance_cos(
        &self,
        connection_id: String,
        vector1: Value,
        vector2: Value,
    ) -> Result<f64> {
        #[derive(serde::Serialize)]
        struct VectorDistanceRequest {
            connection_id: String,
            vector1: Value,
            vector2: Value,
        }

        self.0
            .run_mobile_plugin(
                "vector_distance_cos",
                VectorDistanceRequest {
                    connection_id,
                    vector1,
                    vector2,
                },
            )
            .map_err(Into::into)
    }

    /// Calculate L2 (Euclidean) distance between two vectors
    pub async fn vector_distance_l2(
        &self,
        connection_id: String,
        vector1: Value,
        vector2: Value,
    ) -> Result<f64> {
        #[derive(serde::Serialize)]
        struct VectorDistanceRequest {
            connection_id: String,
            vector1: Value,
            vector2: Value,
        }

        self.0
            .run_mobile_plugin(
                "vector_distance_l2",
                VectorDistanceRequest {
                    connection_id,
                    vector1,
                    vector2,
                },
            )
            .map_err(Into::into)
    }

    /// Extract vector from binary format to JSON array
    pub async fn vector_extract(
        &self,
        connection_id: String,
        vector_blob: Vec<u8>,
    ) -> Result<Vec<f32>> {
        #[derive(serde::Serialize)]
        struct VectorExtractRequest {
            connection_id: String,
            vector_blob: Vec<u8>,
        }

        self.0
            .run_mobile_plugin(
                "vector_extract",
                VectorExtractRequest {
                    connection_id,
                    vector_blob,
                },
            )
            .map_err(Into::into)
    }

    /// Create a vector index on a table column
    pub async fn create_vector_index(
        &self,
        connection_id: String,
        index_name: String,
        table_name: String,
        column_name: String,
        settings: Option<Vec<String>>,
    ) -> Result<ExecuteResult> {
        #[derive(serde::Serialize)]
        struct CreateVectorIndexRequest {
            connection_id: String,
            index_name: String,
            table_name: String,
            column_name: String,
            settings: Option<Vec<String>>,
        }

        self.0
            .run_mobile_plugin(
                "create_vector_index",
                CreateVectorIndexRequest {
                    connection_id,
                    index_name,
                    table_name,
                    column_name,
                    settings,
                },
            )
            .map_err(Into::into)
    }
}
